package com.thinkingcoder.common.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

import org.springframework.objenesis.instantiator.util.UnsafeUtils;

import lombok.SneakyThrows;
import sun.misc.Unsafe;

public interface FieldUtils {

    static List<Field> findAnnotatedField(Class<?> beanClazz, Class<? extends Annotation> annotationClazz) {
        return findField(beanClazz, f -> f.isAnnotationPresent(annotationClazz));
    }

    static List<Field> findField(Class<?> beanClazz, Function<Field, Boolean> function) {
        List<Field> result = new ArrayList<>();
        while (beanClazz != null) {
            Field[] fields = beanClazz.getDeclaredFields();
            for (Field f : fields) {
                if (function.apply(f)) {
                    result.add(f);
                }
            }
            beanClazz = beanClazz.getSuperclass();
        }
        return result;
    }

    @SneakyThrows
    static void setFinalField(Object beanClazz, String fieldName, Object value) {
        setFinalField(beanClazz, beanClazz.getClass().getField(fieldName), value);
    }

    static void setFinalField(Object beanClazz, Field field, Object value) {
        Unsafe unsafe = UnsafeUtils.getUnsafe();
        long fieldOffset = unsafe.objectFieldOffset(field);
        unsafe.putObject(beanClazz, fieldOffset, value);
    }

    @SneakyThrows
    static Object getField(Object obj, String fieldName) {
        return getField(obj, obj.getClass().getField(fieldName));
    }

    static Object getField(Object obj, Field field) {
        Unsafe unsafe = UnsafeUtils.getUnsafe();
        long fieldOffset = unsafe.objectFieldOffset(field);
        return unsafe.getObject(obj, fieldOffset);
    }

}
